#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import time, sys, datetime

from app.AbstractStateJob import AbstractStateJob
from app.utils.XDB2Utils import XDB2Utils
from app.models.Q602_L2_SUBPC2002 import Q602_L2_SUBPC2002
from app.models.TimeWindow import TimeWindow


class RCMStateJob(AbstractStateJob):
    """

    """
    # NOTE 默认相邻数据时间差全相差5s
    GAP_OF_SIBLING_BY_DEFAULT = 5

    INCORRECT_TIME_THRESHOLD = 300

    CORRECT_TIMES = 12
    CORRECT_TIME_THRESHOLD = 61

    # NOTE 没102的数据  甚至没schema和表， 暂时先不考虑Q102
    # UNIT_CODES = ['Q102', 'Q202', 'Q302', 'Q402', 'Q502', 'Q602']
    #UNIT_CODES = ['Q202', 'Q302', 'Q402', 'Q502', 'Q602']
    UNIT_CODE_Q502 = 'Q502'

    def __init__(self, p_config=None, p_unit_code: str = None):
        super(RCMStateJob, self).__init__(p_config=p_config, p_unit_code=p_unit_code)
        pass

    def execute(self):
        print('RCMStateJob.execute')

        self.__do_execute()

        # close db
        super(RCMStateJob, self).execute()
        pass

    def __do_execute(self):
        """
        读取停机表（BGTARAS1.T_DWD_WH_ZZSC_JZ_SU_QALL_0002），查找Q412的最后一次停机数据
        select MAX(SD_END) from BGTARAS1.T_DWD_WH_ZZSC_JZ_SU_QALL_0002 where UNIT_CODE='Q412'
        判断是否有数据
        此时会有两种情况
        无记录（首次）, 有记录

        无记录，
        执行
        判断期间发生过几次停机（开始时间，结束时间）
        """
        # NOTE 机组代码。 RCM就时Q102, Q202, Q302, Q402, Q502, Q602, 先写死Q402
        # 第一步就去查了这个机器最后一次停机时间
        # 如果没查到。就全查，或者把开始时间设为当前时间的钱几天
        start_time = (datetime.datetime.now() + datetime.timedelta(days=-38)).strftime('%Y%m%d%H%M%S')
        end_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
        sql = "select MAX(SHUTDOWN_END) as TMP_MAX from BGTARAS1.T_DWD_WH_SBOPSI_JZ_SU_QALL_0002_test where UNIT_CODE='%s'" % (self.unit_code)
        success, results = XDB2Utils.fetchall_sql(p_sql=sql, p_conn=self.conn)
        print('\n第一步就去查了这个机器最后一次停机时间:')
        print(results)
        if success and len(results) > 0 and results[0]['TMP_MAX'] is not None:
            start_time = results[0]['TMP_MAX']

        time_window_list = self.__fetch_all_shutdown_time_windows_in(p_unit_code=self.unit_code, p_start_date=start_time, p_end_date=end_time)
        self.__save_all_shutdown_time_windows_2_db(p_unit_code=self.unit_code, p_time_window_list=time_window_list)

    def __fetch_all_shutdown_time_windows_in(self, p_unit_code: str = None, p_start_date=None, p_end_date=None) -> (list):
        """
        速度为0超过300S  就是第N+1条数据的时间 减去 第N条的数据时间，  若大于300S,  则认为第N条数据是停机开始的时间。  好像应该这么理解。

        然后还有一种情况就是速度是0
        这种你直接当作这条数据就是没收到电文
        不考虑就行
        这样比较好处理
        不然就比较复杂

        没收到电文300秒
        速度为0，300秒
        还有哦就是二者加起来也300秒
        这几种情况都算发生了停机
        所以就直接把速度为0的，，都当作没收到电文处理了

        所以我sql读数据 直接用了个where speed>0


        只要保证时间差300s后，第一次出现连续一分钟得到数据

        结束时间的判定是开始收到速度大于0的数据一分钟
        ————————
        也可以理解为 连续出现了12条数据，  第12条的时间-第1条的时间 为60S，  则第1条是停机结束时间(也就是重新开机的时间)， 对不？
        :return:
        """
        time_window_list = list()
        # 读停机表
        # RCM就时Q102, Q202, Q302, Q402, Q502, Q602
        # 每一个机器都有一个库
        # 然后相同结尾的  逻辑完全一样
        # 所以就先随便选一个

        # NOTE Q502走现在RCM的，QX02(X!=5)走需要改一下SQL的RCM
        if p_unit_code == RCMStateJob.UNIT_CODE_Q502:
            sql = 'Select TOC as DATETIME, MILLSPEED as MILL_SPEED from BGTARO%s.T_BPC_ACTDATA where TOC>%s and TOC<%s and MILLSPEED>0 order by TOC' % (
                p_unit_code, p_start_date, p_end_date)
        else:
            sql = 'Select DATETIME, MILL_SPEED from BGTARO%s.SUBPC2002 where DATETIME>%s and DATETIME<%s and MILL_SPEED>0 order by DATETIME' % (
                p_unit_code, p_start_date, p_end_date)
        success, results = XDB2Utils.fetchall_sql(p_sql=sql, p_conn=self.conn)
        print('\n__fetch_all_shutdown_time_windows_in  读停机表:')
        print('一共有 %d 条停机数据' % (len(results)))
        if success:
            prev_entry = None
            curr_entry = None
            next_entry = None

            # init an invalid time window
            time_window = TimeWindow(p_start_time=None, p_end_time=None)
            pseudo_end_time = None
            correct_times = 0

            for index, r in enumerate(results):
                if index > 0:
                    t = results[index - 1]
                    prev_entry = Q602_L2_SUBPC2002(p_speed=t['MILL_SPEED'], p_date_time=t['DATETIME'])
                curr_entry = Q602_L2_SUBPC2002(p_speed=r['MILL_SPEED'], p_date_time=r['DATETIME'])
                if index < len(results) - 1:
                    t = results[index + 1]
                    next_entry = Q602_L2_SUBPC2002(p_speed=t['MILL_SPEED'], p_date_time=t['DATETIME'])
                else:
                    # NOTE 假冒一个, 假冒的数据让它和上一个数据恰好是5s，即正常时间间隔， 这样可以避免判断循环结束时的边界条件
                    # NOTE 其实第14条和第13条之间是100S，也不应该影响1~13条， 因为1~13已经判断完成了， 第14条不应影响他们了。
                    # NOTE 故
                    time_struct = datetime.datetime.strptime(str(curr_entry.DATE_TIME), '%Y%m%d%H%M%S')
                    offset = datetime.timedelta(seconds=RCMStateJob.GAP_OF_SIBLING_BY_DEFAULT)
                    next_date_str = (time_struct + offset).strftime('%Y%m%d%H%M%S')
                    next_entry = Q602_L2_SUBPC2002(p_speed=sys.maxsize, p_date_time=next_date_str)

                # gap of next and curr in seconds
                time_1_struct = datetime.datetime.strptime(str(curr_entry.DATE_TIME), '%Y%m%d%H%M%S')
                time_2_struct = datetime.datetime.strptime(str(next_entry.DATE_TIME), '%Y%m%d%H%M%S')
                gap1 = (time_2_struct - time_1_struct).seconds

                # NOTE 相邻差300s, 就是停机开始
                if gap1 > RCMStateJob.INCORRECT_TIME_THRESHOLD and time_window.start_time is None:
                    time_window.start_time = curr_entry.DATE_TIME
                    print('index={}, start_time found -->{}, curr.DATE_TIME={}'.format(index, time_window.start_time, curr_entry.DATE_TIME))
                    continue

                # NOTE
                if time_window.start_time is not None:
                    if pseudo_end_time is None:
                        # NOTE 连续13条数据都正常，且GAP==60  然后  这13条的第一条
                        correct_times = 0
                        pseudo_end_time = curr_entry.DATE_TIME
                        time_window.end_time = None
                        print('index={}, pseudo_end_time={}'.format(index, pseudo_end_time))
                        # NOTE 就是你第一条第一次的gap如果不是456  就不会执行第二种情况
                        # NOTE 因为发现了上来第一条就gap2直接220
                        # NOTE 然后之后的都没办法执行了
                        # NOTE 第一个不是5怎么办, 之前报错就是因为第一个不是5, 是220,然后就225 230 235, 然后永远都出不去了.
                        is_in1 = gap1 in range(RCMStateJob.GAP_OF_SIBLING_BY_DEFAULT - 1, RCMStateJob.GAP_OF_SIBLING_BY_DEFAULT + 2, 1)
                        if is_in1 is False:
                            print('出现了第2中情况 index={}, curr.DATE_TIME={}, gap1={}'.format(index, curr_entry.DATE_TIME,
                                                                                         gap1))
                            correct_times = 0
                            pseudo_end_time = None
                            time_window.end_time = None
                            continue
                        continue
                    #
                    correct_times += 1
                    # NOTE 用时间减法, 不能用数字减法

                    # NOTE 如果2个数据相隔时间不是5秒，则认为是缺失数据, 则将correct_times pseudo_end_time清空, 认为之前对end_time的查找是错误的.
                    # NOTE 重新开始找pseudo_end_time
                    # NOTE 里面突然有数据抽风导致相邻数据间隔有零星一两条4s或6s
                    is_in = gap1 in range(RCMStateJob.GAP_OF_SIBLING_BY_DEFAULT - 1, RCMStateJob.GAP_OF_SIBLING_BY_DEFAULT + 2, 1)
                    if is_in is False and correct_times <= RCMStateJob.CORRECT_TIMES:
                        # NOTE 假设当前条正好是第13条; 此处必须确保即使第13~14条之间的gap非法, 也不应中断循环
                        print('出现了第2中情况 index={}, curr.DATE_TIME={}, gap1={}'.format(index, curr_entry.DATE_TIME, gap1))
                        correct_times = 0
                        pseudo_end_time = None
                        time_window.end_time = None
                        continue
                    #
                    time_1_struct = datetime.datetime.strptime(str(pseudo_end_time), '%Y%m%d%H%M%S')
                    time_2_struct = datetime.datetime.strptime(str(curr_entry.DATE_TIME), '%Y%m%d%H%M%S')
                    gap2 = (time_2_struct - time_1_struct).seconds
                    print('index={}, correct_times={}, pseudo_end_time={}, curr.DATE_TIME={}, gap2={}'.format(index, correct_times, pseudo_end_time, curr_entry.DATE_TIME, gap2))
                    # is_in = gap in range(RCMStateJob.CORRECT_TIME_THRESHOLD - 1, RCMStateJob.CORRECT_TIME_THRESHOLD + 1, 1)
                    is_in = gap2 <= RCMStateJob.CORRECT_TIME_THRESHOLD
                    # NOTE 其实61s内有13条就行
                    if correct_times == RCMStateJob.CORRECT_TIMES and is_in:
                        time_window.end_time = pseudo_end_time
                        if time_window.is_valid():
                            time_window_list.append(time_window)
                        print('end_time found -->pseudo_end_time={} start_time={}, end_time={}\n'.format(pseudo_end_time, time_window.start_time, time_window.end_time))
                        # NOTE reset
                        correct_times = 0
                        pseudo_end_time = None
                        time_window = TimeWindow(p_start_time=None, p_end_time=None)

        print('\ntime_window_list  所有停机时间:')
        for element in time_window_list:
            print('%s %s' % (str(element.start_time), str(element.end_time)))
        return time_window_list

    def __save_all_shutdown_time_windows_2_db(self, p_unit_code: str = None, p_time_window_list: list = None) -> (bool):
        """
        # 将其他的信息写入到数据库
        #
        """
        # 分别就是一开始查询的那QXXX，停机开始的日期，小时单位的停机时间差，开始时间，结束时间，分钟单位的停机时间差
        # UNIT_CODE, SHUTDOWN_DATE, SHUTDOWN_TIME, SHUTDOWN_START, SHUTDOWN_END, SHUTDOWN_MINUTE
        p_sql = "INSERT INTO BGTARAS1.T_DWD_WH_SBOPSI_JZ_SU_QALL_0002_test(UNIT_CODE, SHUTDOWN_DATE, SHUTDOWN_TIME, SHUTDOWN_START, SHUTDOWN_END, SHUTDOWN_MINUTE) values('%s','%s','%s','%s','%s','%s')"
        for o in p_time_window_list:
            sql = "select * from BGTARAS1.T_DWD_WH_SBOPSI_JZ_SU_QALL_0002_test where UNIT_CODE='%s' AND SHUTDOWN_START='%s' AND SHUTDOWN_END='%s'" % (
                self.unit_code, str(o.start_time), str(o.end_time))
            success, results = XDB2Utils.fetchall_sql(p_sql=sql, p_conn=self.conn)
            if len(results) == 0:
                param = list()
                SHUTDOWN_DATE = datetime.datetime.strptime(str(o.start_time), '%Y%m%d%H%M%S').strftime('%Y%m%d')  # 停机开始的日期
                # NOTE 用时间减法, 不能用数字减法
                # SHUTDOWN_MINUTE = (o.end_time - o.start_time) / 60  # 分钟单位的停机时间差
                time_2_struct = datetime.datetime.strptime(str(o.end_time), '%Y%m%d%H%M%S')
                time_1_struct = datetime.datetime.strptime(str(o.start_time), '%Y%m%d%H%M%S')
                SHUTDOWN_MINUTE = (time_2_struct - time_1_struct).seconds / 60
                SHUTDOWN_TIME = SHUTDOWN_MINUTE / 60  # 小时单位的停机时间差
                t = (str(p_unit_code),
                     str(SHUTDOWN_DATE),
                     str(SHUTDOWN_TIME),
                     str(o.start_time),
                     str(o.end_time),
                     str(SHUTDOWN_MINUTE))
                param.append(t)
            success = XDB2Utils.executemany_sql(p_sql=p_sql, p_conn=self.conn, p_param=param)
        return True
